﻿using System;
using System.IO;

namespace ExFlash
{
    public class BinaryBitReader : BinaryReader
    {
        public BinaryBitReader(Stream st)
            : base(st)
        {

        }

        public long Position
        {
            get
            {
                return BaseStream.Position;
            }

            set
            {
                BitOffset = 0;
                BaseStream.Position = value;
            }
        }

        public int BitOffset { get; private set; }

        public int ReadInt32(int count)
        {
            int retValue = (int)ReadUInt32(count);

            if ((retValue & (1L << count - 1)) > 0)
            {
                retValue |= -1 << count;
            }

            return retValue;
        }

        public uint ReadUInt32(int count)
        {
            if (count > 32 || count <= 0)
                return 0;

            uint retValue = 0;
            int firstCount = count;
            Byte bitBuf;

            if (BitOffset + count >= 8)
            {
                bitBuf = (byte)(ReadByte() & (0xFF >> BitOffset));
            }
            else
            {
                bitBuf = (byte)(PeekByte() & (0xFF >> BitOffset));
            }

            int bitPos = 8 - BitOffset;

            while (count > bitPos)
            {
                count -= bitPos;
                retValue |= (uint)bitBuf << count;
                
                bitBuf = (count < 8) ? PeekByte() : ReadByte();

                bitPos = 8;
            }

            bitPos -= count;
            retValue |= (uint)bitBuf >> bitPos;

            BitOffset = (BitOffset + firstCount) % 8;

            return retValue;
        }

        public uint PeekUInt32()
        {
            uint temp = ReadUInt32();
            BaseStream.Position -= 4;
            return temp;
        }

        public uint PeekUInt16()
        {
            uint temp = ReadUInt16();
            BaseStream.Position -= 2;
            return temp;
        }

        public float ReadFloat(int count)
        {
            uint temp = ReadUInt32(count);

            return (float)((temp >> 16) + ((temp & 0xFFFF) / Math.Pow(2, 16)));
        }

        public override Boolean ReadBoolean()
        {
            Boolean retValue = ((PeekByte() >> (7 - BitOffset)) & 0x1) > 0;
            BitOffset++;

            if (BitOffset == 8)
            {
                BitOffset = 0;
                BaseStream.Position++;
            }

            return retValue;
        }

        public Byte PeekByte()
        {
            return PeekByte(false);
        }

        public Byte PeekByte(Boolean considerBitOffset)
        {
            if (!considerBitOffset || BitOffset == 0)
            {
                Byte retValue = ReadByte();
                BaseStream.Position--;
                return retValue;
            }

            Byte byte1 = ReadByte();
            Byte byte2 = ReadByte();
            BaseStream.Position -= 2;
            return (Byte)(((byte1 & (0xFF >> BitOffset)) << BitOffset) | byte2 >> (8 - BitOffset));
        }

        public Boolean PeekBoolean()
        {
            return ((PeekByte() >> (7 - BitOffset)) & 0x1) > 0;
        }

        public void SkipBits(int count)
        {
            BitOffset += count;

            if (BitOffset >= 8)
            {
                BaseStream.Position += (BitOffset / 8);
                BitOffset %= 8;
            }
        }

        public void AlignBits()
        {
            if (BitOffset > 0)
            {
                BaseStream.Position++;
                BitOffset = 0;
            }
        }
    }
}
